#--------------------------------------------------------#
#                   Libraries                            #
#--------------------------------------------------------#
set.seed(2020)
#library(ranger)
#library(dplyr)
#library(caret)
library(tidymodels)
library(usemodels)
#--------------------------------------------------------#
#                    Data                                 #
#--------------------------------------------------------#
FS1_T0_Cluster_Assignment <-read.csv("D:\\DKE\\Thesis_related\\Implementation\\Hyper_tuning_results\\FS1_T0_Cluster_Assignment.csv")

#Convert Cluster ID from int to factor
FS1_T0_Cluster_Assignment<- FS1_T0_Cluster_Assignment %>% mutate(Cluster_ID=as.factor(Cluster_ID))
#--------------------------------------------------------#
There were 12 warnings (use warnings() to see them)
#                  Slitting Data                                 #
#--------------------------------------------------------#
set.seed(123)
#ikea_split
FS1_T0_Cluster_split <- initial_split(FS1_T0_Cluster_Assignment, strata = Cluster_ID)
FS1_T0_Cluster_train <- training(FS1_T0_Cluster_split)
FS1_T0_Cluster_test <- testing(FS1_T0_Cluster_split)

set.seed(234)
FS1_T0_Cluster_folds <- bootstraps(FS1_T0_Cluster_train, strata = Cluster_ID)
FS1_T0_Cluster_folds
# Bootstrap sampling using stratification 
#library(usemodels)
use_ranger(Cluster_ID ~ ., data = FS1_T0_Cluster_train)
ranger_recipe <- 
  recipe(formula = Cluster_ID ~ ., data = FS1_T0_Cluster_train) 

ranger_spec <- 
  rand_forest(mtry = tune(), min_n = tune(), trees = 1000) %>% 
  set_mode("classification") %>% 
  set_engine("ranger") 

ranger_workflow <- 
  workflow() %>% 
  add_recipe(ranger_recipe) %>% 
  add_model(ranger_spec) 

set.seed(68322)
ranger_tune <-
  tune_grid(ranger_workflow, resamples = stop("add your rsample object"), grid = stop("add number of candidate points"))

#--------------------------------------------------------#
#                  Start tuning                          #
#--------------------------------------------------------#
ranger_recipe <- 
  recipe(formula = Cluster_ID ~ ., data = FS1_T0_Cluster_train) %>% update_role(Instance_ID, new_role = "ID") 

ranger_spec <- 
  rand_forest(mtry = tune(), min_n = tune(), trees = 1000) %>% 
  set_mode("classification") %>% 
  set_engine("ranger") 

ranger_workflow <- 
  workflow() %>% 
  add_recipe(ranger_recipe) %>% 
  add_model(ranger_spec) 

ranger_folds <- vfold_cv(FS1_T0_Cluster_train)
set.seed(68322)
ranger_tune <-
  tune_grid(ranger_workflow, resamples = ranger_folds, grid = 20)
ranger_tune %>% collect_metrics()
#--------------------------------------------------------#
There were 14 warnings (use warnings() to see them)
#            graphical results                           #
#--------------------------------------------------------#

ranger_tune %>%
  collect_metrics() %>%
  filter(.metric == "roc_auc") %>%
  mutate(min_n = factor(min_n)) %>%
  ggplot(aes(mtry, mean, color = min_n)) +
  geom_line(alpha = 0.5, size = 1.5) +
  geom_point() +
  labs(y = "AUC")

# Specifying the mtry and min_n range to understand better
rf_grid_ranger <- grid_regular(
  mtry(range = c(10, 30)),
  min_n(range = c(2, 8)),
  levels = 5
)

rf_grid_ranger

doParallel::registerDoParallel()

ranger_tune <-
  tune_grid(ranger_workflow, resamples = ranger_folds, grid = rf_grid_ranger)
show_best(ranger_tune, metric = "roc_auc")
show_best(ranger_tune, metric = "accuracy")

select_best(ranger_tune,metric = "roc_auc")
autoplot(ranger_tune)

#--------------------------------------------------------#
Warning messages:
1: In readChar(file, size, TRUE) : truncating string with embedded nuls
2: In readChar(file, size, TRUE) : truncating string with embedded nuls
3: In readChar(file, size, TRUE) : truncating string with embedded nuls
#            graphical results                           #
#--------------------------------------------------------#

ranger_tune %>%
  collect_metrics() %>%
  filter(.metric == "roc_auc") %>%
  mutate(min_n = factor(min_n)) %>%
  ggplot(aes(mtry, mean, color = min_n)) +
  geom_line(alpha = 0.5, size = 1.5) +
  geom_point() +
  labs(y = "AUC")

#--------------------------------------------------------#
Warning messages:
1: In readChar(file, size, TRUE) : truncating string with embedded nuls
2: In readChar(file, size, TRUE) : truncating string with embedded nuls
3: In readChar(file, size, TRUE) : truncating string with embedded nuls
4: In readChar(file, size, TRUE) : truncating string with embedded nuls
#       Show and select the best                         #
#--------------------------------------------------------#

show_best(ranger_tune, metric = "roc_auc")
show_best(ranger_tune, metric = "accuracy")

select_best(ranger_tune,metric = "roc_auc")
autoplot(ranger_tune)

#--------------------------------------------------------#
#       finalize the model and workflow                  #
#--------------------------------------------------------#

final_rf <- ranger_workflow %>%
  finalize_workflow(select_best(ranger_tune))
No value of `metric` was given; metric 'roc_auc' will be used.
final_rf
== Workflow ==============================================================================================================================
Preprocessor: Recipe
Model: rand_forest()

-- Preprocessor --------------------------------------------------------------------------------------------------------------------------
0 Recipe Steps

-- Model ---------------------------------------------------------------------------------------------------------------------------------
Random Forest Model Specification (classification)

Main Arguments:
  mtry = 10
  trees = 1000
  min_n = 2

Computational engine: ranger 

#--------------------------------------------------------#
# fit the best tuned parameter on the training data      #
#--------------------------------------------------------#
FS1_T0_Cluster_fit <- last_fit(final_rf, FS1_T0_Cluster_split)
FS1_T0_Cluster_fit
# Resampling results
# Manual resampling 
collect_metrics(FS1_T0_Cluster_fit)
library(vip)

imp_spec <- ranger_spec %>%
  finalize_model(select_best(ranger_tune)) %>%
  set_engine("ranger", importance = "permutation")
No value of `metric` was given; metric 'roc_auc' will be used.
workflow() %>%
  add_recipe(ranger_recipe) %>%
  add_model(imp_spec) %>%
  fit(FS1_T0_Cluster_train) %>%
  pull_workflow_fit() %>%
  vip(aesthetics = list(alpha = 0.8, fill = "midnightblue"))


Ranger package

tidy_ranger_model <- ranger(Cluster_ID ~ ., data = FS1_T0_Cluster_train[,-1] , importance = "permutation", 
                            local.importance = TRUE,mtry = 10,num.trees = 1000,classification = TRUE,
                            min.node.size = 2)
tidy_ranger_model
Ranger result

Call:
 ranger(Cluster_ID ~ ., data = FS1_T0_Cluster_train[, -1], importance = "permutation",      local.importance = TRUE, mtry = 10, num.trees = 1000, classification = TRUE,      min.node.size = 2) 

Type:                             Classification 
Number of trees:                  1000 
Sample size:                      1306 
Number of independent variables:  44 
Mtry:                             10 
Target node size:                 2 
Variable importance mode:         permutation 
Splitrule:                        gini 
OOB prediction error:             26.49 % 

Train accuracy

confusionMatrix(tidy_ranger_model$confusion.matrix)
Confusion Matrix and Statistics

    predicted
true   1   2   3
   1 268 114   8
   2  75 479  57
   3   2  90 213

Overall Statistics
                                          
               Accuracy : 0.7351          
                 95% CI : (0.7102, 0.7588)
    No Information Rate : 0.523           
    P-Value [Acc > NIR] : < 2.2e-16       
                                          
                  Kappa : 0.5773          
                                          
 Mcnemar's Test P-Value : 0.0002662       

Statistics by Class:

                     Class: 1 Class: 2 Class: 3
Sensitivity            0.7768   0.7013   0.7662
Specificity            0.8730   0.7881   0.9105
Pos Pred Value         0.6872   0.7840   0.6984
Neg Pred Value         0.9159   0.7065   0.9351
Prevalence             0.2642   0.5230   0.2129
Detection Rate         0.2052   0.3668   0.1631
Detection Prevalence   0.2986   0.4678   0.2335
Balanced Accuracy      0.8249   0.7447   0.8383

Test accuracy

tidy_ranger_pred.data <- predict(tidy_ranger_model, data = FS1_T0_Cluster_test)
table(FS1_T0_Cluster_test$Cluster_ID, tidy_ranger_pred.data$predictions)
   
      1   2   3
  1  86  44   1
  2  30 161  13
  3   0  43  59
tidy_ranger_cm<-confusionMatrix(table(FS1_T0_Cluster_test$Cluster_ID, tidy_ranger_pred.data$predictions, dnn = c("Reference", "Prediction")))
tidy_ranger_cm
Confusion Matrix and Statistics

         Prediction
Reference   1   2   3
        1  86  44   1
        2  30 161  13
        3   0  43  59

Overall Statistics
                                          
               Accuracy : 0.7002          
                 95% CI : (0.6549, 0.7428)
    No Information Rate : 0.5675          
    P-Value [Acc > NIR] : 7.837e-09       
                                          
                  Kappa : 0.5138          
                                          
 Mcnemar's Test P-Value : 0.000194        

Statistics by Class:

                     Class: 1 Class: 2 Class: 3
Sensitivity            0.7414   0.6492   0.8082
Specificity            0.8598   0.7725   0.8819
Pos Pred Value         0.6565   0.7892   0.5784
Neg Pred Value         0.9020   0.6266   0.9582
Prevalence             0.2654   0.5675   0.1670
Detection Rate         0.1968   0.3684   0.1350
Detection Prevalence   0.2998   0.4668   0.2334
Balanced Accuracy      0.8006   0.7108   0.8450
tidy(tidy_ranger_cm)

Conclusion : Model accuracy

LS0tDQp0aXRsZTogIlRpZHkgbW9kZWxzIGFsb25nIHdpdGggcmFuZ2VyIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KDQpgYGB7cn0NCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIw0KIyAgICAgICAgICAgICAgICAgICBMaWJyYXJpZXMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIw0Kc2V0LnNlZWQoMjAyMCkNCiNsaWJyYXJ5KHJhbmdlcikNCiNsaWJyYXJ5KGRwbHlyKQ0KI2xpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KHRpZHltb2RlbHMpDQpsaWJyYXJ5KHVzZW1vZGVscykNCmBgYA0KDQoNCg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0jDQojICAgICAgICAgICAgICAgICAgICBEYXRhICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIw0KRlMxX1QwX0NsdXN0ZXJfQXNzaWdubWVudCA8LXJlYWQuY3N2KCJEOlxcREtFXFxUaGVzaXNfcmVsYXRlZFxcSW1wbGVtZW50YXRpb25cXEh5cGVyX3R1bmluZ19yZXN1bHRzXFxGUzFfVDBfQ2x1c3Rlcl9Bc3NpZ25tZW50LmNzdiIpDQoNCiNDb252ZXJ0IENsdXN0ZXIgSUQgZnJvbSBpbnQgdG8gZmFjdG9yDQpGUzFfVDBfQ2x1c3Rlcl9Bc3NpZ25tZW50PC0gRlMxX1QwX0NsdXN0ZXJfQXNzaWdubWVudCAlPiUgbXV0YXRlKENsdXN0ZXJfSUQ9YXMuZmFjdG9yKENsdXN0ZXJfSUQpKQ0KYGBgDQoNCg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0jDQojICAgICAgICAgICAgICAgICAgU2xpdHRpbmcgRGF0YSAgICAgICAgICAgICAgICAgICAgICAgICAjDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0jDQpzZXQuc2VlZCgxMjMpDQojaWtlYV9zcGxpdA0KRlMxX1QwX0NsdXN0ZXJfc3BsaXQgPC0gaW5pdGlhbF9zcGxpdChGUzFfVDBfQ2x1c3Rlcl9Bc3NpZ25tZW50LCBzdHJhdGEgPSBDbHVzdGVyX0lEKQ0KRlMxX1QwX0NsdXN0ZXJfdHJhaW4gPC0gdHJhaW5pbmcoRlMxX1QwX0NsdXN0ZXJfc3BsaXQpDQpGUzFfVDBfQ2x1c3Rlcl90ZXN0IDwtIHRlc3RpbmcoRlMxX1QwX0NsdXN0ZXJfc3BsaXQpDQoNCnNldC5zZWVkKDIzNCkNCkZTMV9UMF9DbHVzdGVyX2ZvbGRzIDwtIGJvb3RzdHJhcHMoRlMxX1QwX0NsdXN0ZXJfdHJhaW4sIHN0cmF0YSA9IENsdXN0ZXJfSUQpDQpGUzFfVDBfQ2x1c3Rlcl9mb2xkcw0KYGBgDQoNCg0KYGBge3J9DQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSMNCiMgdXNlX3JhbmdlciA6IHdpbGwgZ2l2ZSB3b3JrZmxvdyBwaXBlbGluZSBkZXRhaWxzICAgICAgICMNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSMNCiNsaWJyYXJ5KHVzZW1vZGVscykNCnVzZV9yYW5nZXIoQ2x1c3Rlcl9JRCB+IC4sIGRhdGEgPSBGUzFfVDBfQ2x1c3Rlcl90cmFpbikNCmBgYA0KDQoNCg0KDQpgYGB7cn0NCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIw0KIyAgICAgICAgICAgICAgICAgIFN0YXJ0IHR1bmluZyAgICAgICAgICAgICAgICAgICAgICAgICAgIw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIw0KcmFuZ2VyX3JlY2lwZSA8LSANCiAgcmVjaXBlKGZvcm11bGEgPSBDbHVzdGVyX0lEIH4gLiwgZGF0YSA9IEZTMV9UMF9DbHVzdGVyX3RyYWluKSAlPiUgdXBkYXRlX3JvbGUoSW5zdGFuY2VfSUQsIG5ld19yb2xlID0gIklEIikgDQoNCnJhbmdlcl9zcGVjIDwtIA0KICByYW5kX2ZvcmVzdChtdHJ5ID0gdHVuZSgpLCBtaW5fbiA9IHR1bmUoKSwgdHJlZXMgPSAxMDAwKSAlPiUgDQogIHNldF9tb2RlKCJjbGFzc2lmaWNhdGlvbiIpICU+JSANCiAgc2V0X2VuZ2luZSgicmFuZ2VyIikgDQoNCnJhbmdlcl93b3JrZmxvdyA8LSANCiAgd29ya2Zsb3coKSAlPiUgDQogIGFkZF9yZWNpcGUocmFuZ2VyX3JlY2lwZSkgJT4lIA0KICBhZGRfbW9kZWwocmFuZ2VyX3NwZWMpIA0KDQpyYW5nZXJfZm9sZHMgPC0gdmZvbGRfY3YoRlMxX1QwX0NsdXN0ZXJfdHJhaW4pDQpzZXQuc2VlZCg2ODMyMikNCnJhbmdlcl90dW5lIDwtDQogIHR1bmVfZ3JpZChyYW5nZXJfd29ya2Zsb3csIHJlc2FtcGxlcyA9IHJhbmdlcl9mb2xkcywgZ3JpZCA9IDIwKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSMNCiMgICAgVHVuZWQgcmVzdWx0cyBhbmQgdmFyaW91cyBtZXRyaWNzICAgICAgICAgICAgICAgICAgICMNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSMNCnJhbmdlcl90dW5lICU+JSBjb2xsZWN0X21ldHJpY3MoKQ0KYGBgDQoNCg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0jDQojICAgICAgICAgICAgZ3JhcGhpY2FsIHJlc3VsdHMgICAgICAgICAgICAgICAgICAgICAgICAgICAjDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0jDQoNCnJhbmdlcl90dW5lICU+JQ0KICBjb2xsZWN0X21ldHJpY3MoKSAlPiUNCiAgZmlsdGVyKC5tZXRyaWMgPT0gInJvY19hdWMiKSAlPiUNCiAgbXV0YXRlKG1pbl9uID0gZmFjdG9yKG1pbl9uKSkgJT4lDQogIGdncGxvdChhZXMobXRyeSwgbWVhbiwgY29sb3IgPSBtaW5fbikpICsNCiAgZ2VvbV9saW5lKGFscGhhID0gMC41LCBzaXplID0gMS41KSArDQogIGdlb21fcG9pbnQoKSArDQogIGxhYnMoeSA9ICJBVUMiKQ0KYGBgDQoNCg0KYGBge3J9DQojIFNwZWNpZnlpbmcgdGhlIG10cnkgYW5kIG1pbl9uIHJhbmdlIHRvIHVuZGVyc3RhbmQgYmV0dGVyDQpyZl9ncmlkX3JhbmdlciA8LSBncmlkX3JlZ3VsYXIoDQogIG10cnkocmFuZ2UgPSBjKDEwLCAzMCkpLA0KICBtaW5fbihyYW5nZSA9IGMoMiwgOCkpLA0KICBsZXZlbHMgPSA1DQopDQoNCnJmX2dyaWRfcmFuZ2VyDQoNCmRvUGFyYWxsZWw6OnJlZ2lzdGVyRG9QYXJhbGxlbCgpDQoNCnJhbmdlcl90dW5lIDwtDQogIHR1bmVfZ3JpZChyYW5nZXJfd29ya2Zsb3csIHJlc2FtcGxlcyA9IHJhbmdlcl9mb2xkcywgZ3JpZCA9IHJmX2dyaWRfcmFuZ2VyKQ0KYGBgDQoNCg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0jDQojICAgICAgIFNob3cgYW5kIHNlbGVjdCB0aGUgYmVzdCAgICAgICAgICAgICAgICAgICAgICAgICAjDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0jDQoNCnNob3dfYmVzdChyYW5nZXJfdHVuZSwgbWV0cmljID0gInJvY19hdWMiKQ0Kc2hvd19iZXN0KHJhbmdlcl90dW5lLCBtZXRyaWMgPSAiYWNjdXJhY3kiKQ0KDQpzZWxlY3RfYmVzdChyYW5nZXJfdHVuZSxtZXRyaWMgPSAicm9jX2F1YyIpDQphdXRvcGxvdChyYW5nZXJfdHVuZSkNCg0KYGBgDQoNCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIw0KIyAgICAgICAgICAgIGdyYXBoaWNhbCByZXN1bHRzICAgICAgICAgICAgICAgICAgICAgICAgICAgIw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIw0KDQpyYW5nZXJfdHVuZSAlPiUNCiAgY29sbGVjdF9tZXRyaWNzKCkgJT4lDQogIGZpbHRlcigubWV0cmljID09ICJyb2NfYXVjIikgJT4lDQogIG11dGF0ZShtaW5fbiA9IGZhY3RvcihtaW5fbikpICU+JQ0KICBnZ3Bsb3QoYWVzKG10cnksIG1lYW4sIGNvbG9yID0gbWluX24pKSArDQogIGdlb21fbGluZShhbHBoYSA9IDAuNSwgc2l6ZSA9IDEuNSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBsYWJzKHkgPSAiQVVDIikNCmBgYA0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0jDQojICAgICAgIFNob3cgYW5kIHNlbGVjdCB0aGUgYmVzdCAgICAgICAgICAgICAgICAgICAgICAgICAjDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0jDQoNCnNob3dfYmVzdChyYW5nZXJfdHVuZSwgbWV0cmljID0gInJvY19hdWMiKQ0Kc2hvd19iZXN0KHJhbmdlcl90dW5lLCBtZXRyaWMgPSAiYWNjdXJhY3kiKQ0KDQpzZWxlY3RfYmVzdChyYW5nZXJfdHVuZSxtZXRyaWMgPSAicm9jX2F1YyIpDQphdXRvcGxvdChyYW5nZXJfdHVuZSkNCmBgYA0KDQoNCmBgYHtyfQ0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIw0KIyAgICAgICBmaW5hbGl6ZSB0aGUgbW9kZWwgYW5kIHdvcmtmbG93ICAgICAgICAgICAgICAgICAgIw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIw0KDQpmaW5hbF9yZiA8LSByYW5nZXJfd29ya2Zsb3cgJT4lDQogIGZpbmFsaXplX3dvcmtmbG93KHNlbGVjdF9iZXN0KHJhbmdlcl90dW5lKSkNCg0KZmluYWxfcmYNCmBgYA0KDQpgYGB7cn0NCg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIw0KIyBmaXQgdGhlIGJlc3QgdHVuZWQgcGFyYW1ldGVyIG9uIHRoZSB0cmFpbmluZyBkYXRhICAgICAgIw0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIw0KRlMxX1QwX0NsdXN0ZXJfZml0IDwtIGxhc3RfZml0KGZpbmFsX3JmLCBGUzFfVDBfQ2x1c3Rlcl9zcGxpdCkNCkZTMV9UMF9DbHVzdGVyX2ZpdA0KDQpgYGANCg0KDQpgYGB7cn0NCmNvbGxlY3RfbWV0cmljcyhGUzFfVDBfQ2x1c3Rlcl9maXQpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KHZpcCkNCg0KaW1wX3NwZWMgPC0gcmFuZ2VyX3NwZWMgJT4lDQogIGZpbmFsaXplX21vZGVsKHNlbGVjdF9iZXN0KHJhbmdlcl90dW5lKSkgJT4lDQogIHNldF9lbmdpbmUoInJhbmdlciIsIGltcG9ydGFuY2UgPSAicGVybXV0YXRpb24iKQ0KDQp3b3JrZmxvdygpICU+JQ0KICBhZGRfcmVjaXBlKHJhbmdlcl9yZWNpcGUpICU+JQ0KICBhZGRfbW9kZWwoaW1wX3NwZWMpICU+JQ0KICBmaXQoRlMxX1QwX0NsdXN0ZXJfdHJhaW4pICU+JQ0KICBwdWxsX3dvcmtmbG93X2ZpdCgpICU+JQ0KICB2aXAoYWVzdGhldGljcyA9IGxpc3QoYWxwaGEgPSAwLjgsIGZpbGwgPSAibWlkbmlnaHRibHVlIikpDQpgYGANCg0KDQotLS0NCiMgUmFuZ2VyIHBhY2thZ2UgDQoqIEFib3ZlIHR1bmVkIHZhbHVlcyB1c2VkIGZvciByYW5nZXIgbW9kZWwuIDwvYnI+DQoqIFJhbmdlciBpcyBhIGZhc3QgaW1wbGVtZW50YXRpb24gb2YgcmFuZG9tIGZvcmVzdHMgKEJyZWltYW4gMjAwMSkgb3IgcmVjdXJzaXZlIHBhcnRpdGlvbmluZywgcGFydGljdWxhcmx5IHN1aXRlZCBmb3IgaGlnaCBkaW1lbnNpb25hbCBkYXRhLiA8L2JyPiANCg0KYGBge3J9DQp0aWR5X3Jhbmdlcl9tb2RlbCA8LSByYW5nZXIoQ2x1c3Rlcl9JRCB+IC4sIGRhdGEgPSBGUzFfVDBfQ2x1c3Rlcl90cmFpblssLTFdICwgaW1wb3J0YW5jZSA9ICJwZXJtdXRhdGlvbiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvY2FsLmltcG9ydGFuY2UgPSBUUlVFLG10cnkgPSAxMCxudW0udHJlZXMgPSAxMDAwLGNsYXNzaWZpY2F0aW9uID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4ubm9kZS5zaXplID0gMikNCmBgYA0KDQpgYGB7cn0NCnRpZHlfcmFuZ2VyX21vZGVsDQpgYGANCg0KDQojIFRyYWluIGFjY3VyYWN5DQoNCmBgYHtyfQ0KY29uZnVzaW9uTWF0cml4KHRpZHlfcmFuZ2VyX21vZGVsJGNvbmZ1c2lvbi5tYXRyaXgpDQpgYGANCg0KIyBUZXN0IGFjY3VyYWN5DQpgYGB7cn0NCnRpZHlfcmFuZ2VyX3ByZWQuZGF0YSA8LSBwcmVkaWN0KHRpZHlfcmFuZ2VyX21vZGVsLCBkYXRhID0gRlMxX1QwX0NsdXN0ZXJfdGVzdCkNCnRhYmxlKEZTMV9UMF9DbHVzdGVyX3Rlc3QkQ2x1c3Rlcl9JRCwgdGlkeV9yYW5nZXJfcHJlZC5kYXRhJHByZWRpY3Rpb25zKQ0KDQpgYGANCg0KYGBge3J9DQp0aWR5X3Jhbmdlcl9jbTwtY29uZnVzaW9uTWF0cml4KHRhYmxlKEZTMV9UMF9DbHVzdGVyX3Rlc3QkQ2x1c3Rlcl9JRCwgdGlkeV9yYW5nZXJfcHJlZC5kYXRhJHByZWRpY3Rpb25zLCBkbm4gPSBjKCJSZWZlcmVuY2UiLCAiUHJlZGljdGlvbiIpKSkNCnRpZHlfcmFuZ2VyX2NtDQp0aWR5KHRpZHlfcmFuZ2VyX2NtKQ0KYGBgDQoNCg0KIyBDb25jbHVzaW9uIDogTW9kZWwgYWNjdXJhY3kNCg0KKiBUcmFpbiBBY2N1cmFjeSA6IDczLjY2JSA8L2JyPg0KKiBUZXN0IEFjY3VyYWN5IDogNzAuMDIlIDwvYnI+DQoqIEJvdGggYXJlIGNsb3NlIGJ5LiBCYWxhbmNlZCBtb2RlbC4gPC9icj4=